﻿using System;

namespace SkeinLib
{
    public class Skein512Prng
    {
        const int WORDWIDTH = 8;
        internal UInt64[] _state = null;

        /// <summary>
        /// Initialize the PRNG with a seed and a NONCE.
        /// If the same seed AND NONCE are used on two different PRNG objects, they will produce the same results under most circumstances.
        /// NONCEs as a rule should NEVER be repeated or reused in a production environment.
        /// </summary>
        /// <param name="seed">The seed, as an array of bytes.  Cannot be null or zero length!</param>
        /// <param name="nonce">A nonce to tweak the PRNG further.</param>
        public void Initialize(byte[] seed, byte[] nonce = null)
        {
            //error checks done in Core
            _state = SkeinCore.InitializePrng(WORDWIDTH, seed, nonce);
        }

        /// <summary>
        /// Update the PRNG with additional data.
        /// </summary>
        /// <param name="data">The data, as an array of bytes, to inject into the PRNG.  Cannot be null or zero length!</param>
        public void Update(byte[] data)
        {
            if (_state == null)
                throw new InvalidOperationException("Update:  PRNG not initialized, call Initialize first");
            SkeinCore.ProcessMessage(ref _state, data);
        }

        /// <summary>
        /// Request from the PRNG a number of psuedo-random bytes.
        /// </summary>
        /// <param name="count">How many bytes to be returned.  Must be positive non-zero integer!</param>
        /// <returns>A byte array of specified length.</returns>
        public byte[] GetBytes(int count)
        {
            if (count < 1)
                throw new ArgumentOutOfRangeException("count", "GetBytes:  count cannot be zero or negative");
            if (_state == null)
                throw new InvalidOperationException("GetBytes:  PRNG not initialized, call Initialize first");
            return SkeinCore.ProcessRngOutput(ref _state, count);
        }

        /// <summary>
        /// Wipe out the state to prevent leaking information.
        /// The object must be reinitialized afterwards to produce more psuedo-random bytes.
        /// </summary>
        public void Clear()
        {
            SkeinCore.ClearWords(ref _state, true);
        }
    }
}
